home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-10-16 | 8.7 KB | 219 lines | [TEXT/MPS ] |
- {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
- { UFailure.p }
- { Copyright © 1985-1990 by Apple Computer, Inc. All rights reserved. }
-
- {[f-]}
- (*
- T H E O R Y O F O P E R A T I O N
-
- This unit implements the MacApp failure mechanism.
-
- The failure mechanism is built around exception handlers. An exception
- handler is a routine, generally local to some other routine, that is
- called when a failure occurs and takes action to handle the failure.
- An exception handler is of the form
-
- PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
-
- where error is the error that caused the failure, and message identifies
- the error message that may be displayed. Consider a routine that opens
- a file, reads its contents, and closes the file. If a failure occured
- while reading the file, an exception handler would be needed to close the
- file, as the rest of the routine will not be executed. (See the example
- at the end of these comments.)
-
- References to expection handlers are defined by the FailInfo record.
- The exception handlers form a linked-list via the nextInfo field of
- FailInfo. The linked list is a stack since new exception handlers
- are added to the front of the list.
-
- New exception handlers are added to the stack with the CatchFailures
- procedure, and removed from the stack with the Success procedure. In
- general you call CatchFailures to post an exception handler when an
- error the application should handle might occur, and call Success to
- remove the handler from the stack, after the handler is no longer
- needed (i.e. the possibility of error no longer exists). Any failure
- detected within the limits of the CatchFailures and subsequent Success
- call results in the execution of the exception handler. (Failure does
- not have to occur in the same routine as your call to CatchFailures.
- The failure may occur in any routine called after CatchFailures but
- before Success.)
-
- When MacApp (or your code) determines that a failure has occured, it
- calls Failure. As a convenience, several procedures are provided
- to check for standard kinds of failures and call Failure if needed.
- These procedures are:
-
- FailNIL Calls Failure if its parameter is NIL.
- FailOSErr Calls Failure if its parameter is not noErr.
- FailMemError Calls Failure if MemError returns other than noErr.
- FailResError Calls Failure if ResError returns other than noErr.
-
- When Failure is called, execution of the routine that called Failure is
- terminated and the exception handler at the top of the stack is popped.
- For each routine that was called after the handler was posted
- to the stack, execution is terminated as though from an EXIT statement.
- Then the exception handler is called. It generally cleans up for the
- routine in which it is nested. Upon completion the next exception handler
- is popped from the stack, repeating the process.
-
- The error causing the failure, and a message code is passed to Failure.
- For MacApp, the last exception handler on the stack is the one in
- TApplication.PollEvent. It calls TApplication.ShowError, which calls
- ErrorAlert, which decodes the message and displays an alert. You exception
- handlers may set the message code to one more specific to your application
- by calling FailNewMessage at the end of your exception handler.
- FailNewMessage changes the message only if the current one is non-zero.
- This has the effect of allowing those exception handlers closest to the
- source of the error to set the message.
-
- One last note about exception handlers: It is possible for an exception
- handler to terminate exception processing by using a non-local GOTO to
- jump back into the routine in which the exception handler is nested. This
- is how MacApp keeps the application running when a failure occurs. The
- last exception handler on the stack, in TApplication.PollEvent, uses a
- GOTO to continue event processing.
-
- The following is an example showing the use of exception handlers.
- The ReadTheFile procedure reads the contents of a file, signalling
- failure if the open, read, or close returns an error.
-
- PROCEDURE ReadTheFile (fileName: Str255; volRefNum: INTEGER;
- VAR contents: ContentsRecord);
- VAR
- fi: FailInfo;
- count: LONGINT;
-
- PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
- VAR
- err: OSErr;
- BEGIN
- err := FSClose(fileRefNum); { Make sure the file is closed }
- { Pass on my own error message... }
- FailNewMessage(error, message, kMyErrorMessage);
- END;
-
- BEGIN
- { If FSOpen fails we don't need to do any failure handling,
- though the guy that called us probably will. }
- FailOSErr(FSOpen(fileName, volRefNum, fileRefNum));
-
- { Now that the file is open, if the read fails we must make
- sure to close the file. }
- CatchFailures(fi, Exceptionhandler);
-
- count := SIZEOF(ContentsRecord);
- FailOSErr(FSRead(fileRefNum, count, @contents));
-
- { The file's been read so we don't need our exception handler
- anymore. }
- Success(fi);
-
- { Again, the guy that called us will have to deal with this. }
- FailOSErr(FSClose(fileRefNum));
- END;
- *)
- {[f+]}
-
- UNIT UFailure;
-
- INTERFACE
-
- USES Types, Memory, Resources, Errors;
-
- CONST
- minErr = - 32768; { Low error number }
- maxErr = 32767; { High error number }
-
- TYPE
- { used for the exception handling mechanism }
- FailInfoPtr = ^FailInfo; { Preferred. The pointer
- type _MUST_ be declared first since the record
- is self referential }
- FailInfo = RECORD
- regs: ARRAY [1..11] OF LONGINT; { The saved registers as of the
- CatchFailures. }
- error: INTEGER; { The error condition passed to Failure. }
- message: LONGINT; { The message which accompanied the Failure.
- }
- failA6: LONGINT; { A6 of the caller to CatchFailures. }
- failPC: LONGINT; { Address of failure handler routine. }
- nextInfo: FailInfoPtr; { Next in stack. }
- END;
- PFailInfo = FailInfoPtr; { Left in for compatibility (2.0) }
-
- VAR
- {$Push} {$J+} { Defined externally }
- gTopHandler: FailInfoPtr; { Most recent link in linked list of failure
- handlers. Set to nil by a constant in
- UFailure.a so that Failure handling never
- needs to be initialized. }
- {$Pop}
-
- PROCEDURE Assertion(condition: BOOLEAN;
- description: StringPtr);
- { Asserts a condition.If condition is FALSE : IF debugging : prints description and stops in
- Debugger ELSE : Signals a general failure. }
-
- FUNCTION BuildMessage(lowWord, highWord: INTEGER): LONGINT;
- INLINE $2E9F; { MOVE.L (A7)+,(A7) }
- { Takes the 2 integers and combines them into a LONGINT failure message. Note that the
- low-order word is the first parameter. }
-
- PROCEDURE CatchFailures(VAR fi: FailInfo;
- PROCEDURE Handler(e: INTEGER;
- m: LONGINT));
- { Call this to set up an exception handler. This pushes your handler onto
- a stack of exception handlers. }
-
- PROCEDURE EachFailureHandlerDo(PROCEDURE DoToHandler(fiPtr: FailInfoPtr));
- { Calls DoToHandler for each failure handler in the stack from gTopHandler
- to the outermost handler. }
-
- PROCEDURE Failure(error: INTEGER;
- message: LONGINT);
- { Call this to signal a failure. Control will branch to the most recent
- exception handler, which will be popped off the handler stack. }
-
- PROCEDURE FailMemError;
- { IF MemError <> noErr THEN Failure(MemError, 0); If you are using
- assembler, then you should just test the return code from the Memory
- Manager in DO by calling FailOSErr. (See the discussion of MemError in
- Inside Macintosh.) }
-
- PROCEDURE FailResError;
- { IF ResError <> noErr THEN Failure(ResError, 0); (See Inside Macintosh.) }
-
- PROCEDURE FailNewMessage(error: INTEGER;
- oldMessage, newMessage: LONGINT);
- { This does:
- IF oldMessage = 0 THEN
- Failure(error, newMessage)
- ELSE
- Failure(error, oldMessage);
- }
-
- PROCEDURE FailNIL(p: UNIV Ptr);
- { Call this with a pointer/handle; this signals Failure(memFullErr, 0) if p is NIL. }
-
- PROCEDURE FailNILResource(r: UNIV Handle);
- { Call this with a resource handle; this signals Failure if the handle is nil. The error is
- either that returned by ResError, or resNotFound if ResError returns no error. }
-
- PROCEDURE FailOSErr(error: INTEGER);
- { Call this with an OSError; signals Failure(error, 0) if error <> noErr. }
-
- FUNCTION HandlerExists(testFailInfoPtr: FailInfoPtr): BOOLEAN;
- { This test returns TRUE if the failure handler exists in the stack from gTopHandler
- to the outermost handler. }
-
- PROCEDURE Success(VAR fi: FailInfo);
- { Call this when you want to pop your exception handler. We assume that the programmer passes
- in the most recent FailInfo record; ie. the one that is the top of the stack. If debugging
- is on, we check to be sure. }
-
- IMPLEMENTATION
-
- {$I UMAFailure.inc1.p}
- END.
-